package ws

import (
	"time"

	"github.com/gorilla/websocket"
	"xgame.go_server/cluster"
	"xgame.go_server/comm/log"
	"xgame.go_server/msg"
)

// 指令上下文
type cmdCtx struct {
	sessionUId   string
	userId       int64
	clientIpAddr string
	clientConn   *websocket.Conn
	sendMsgQ     chan []byte // BlockingQueue
}

// 绑定用户 Id
func (SELF *cmdCtx) BindUserId(val int64) {
	SELF.userId = val
}

// 获取用户 Id
func (SELF *cmdCtx) GetUserId() int64 {
	return SELF.userId
}

// 获取客户端 IP 地址
func (SELF *cmdCtx) GetClientIpAddr() string {
	return SELF.clientIpAddr
}

// 写出消息
func (SELF *cmdCtx) Write(byteArray []byte) {
	if nil == byteArray ||
		nil == SELF.clientConn ||
		nil == SELF.sendMsgQ {
		return
	}

	SELF.sendMsgQ <- byteArray // queue.push
}

// 发送错误
func (SELF *cmdCtx) SendError(errorCode int, errorInfo string) {
}

// 断开连接
func (SELF *cmdCtx) Disconnect() {
	if nil != SELF.clientConn {
		_ = SELF.clientConn.Close()
	}
}

// 开始循环发送消息, 内部通过协程来实现
func (SELF *cmdCtx) startLoopSendMsg() {
	// 首先构建发送队列
	SELF.sendMsgQ = make(chan []byte, 64)

	go func() { // new Thread().start(() -> { ... })
		for {
			byteArray := <-SELF.sendMsgQ // queue.pop

			if nil == byteArray {
				continue
			}

			func() {
				defer func() {
					if err := recover(); nil != err {
						log.Error("发生异常, %+v", err)
					}
				}()

				if err := SELF.clientConn.WriteMessage(websocket.BinaryMessage, byteArray); nil != err {
					log.Error("%+v", err)
				}
			}()
		}
	}() // 相当于启动一个线程, 专门负责发送消息
}

// 循环读取消息
func (SELF *cmdCtx) loopReadMsg() {
	if nil == SELF.clientConn {
		return
	}

	// 设置读取字节数限制
	SELF.clientConn.SetReadLimit(64 * 1024)

	t0 := int64(0)
	counter := 0

	//
	// 循环读取游戏客户端发来的消息,
	// 转发给游戏服
	//
	for {
		// 接收游戏客户端发来的消息
		msgType, msgData, err := SELF.clientConn.ReadMessage()

		if nil != err {
			log.Error("%+v", err)
			break
		}

		t1 := time.Now().UnixMilli()

		if (t1 - t0) > oneSecond {
			t0 = t1
			counter = 0
		}

		if counter >= readMsgCountPerSecond {
			log.Error("消息过于频繁")
			continue
		}

		counter++

		msgCode := msg.ReadMsgCode(msgData)
		sjt := getSjtByMsgCode(msgCode)

		var bizServerData *cluster.BizServerData
		var ok bool = false

		switch sjt {
		case cluster.E_Sjt_Login:
			// 如果是登录服务器,
			// 就随机选择一个...
			bizServerData, ok = cluster.GetABizServer_random(sjt)
		case cluster.E_Sjt_Game:
			// 如果是游戏服务器,
			// 就选择一个负载最小的...
			bizServerData, ok = cluster.GetABizServer_byLeastLoad(sjt)
		}

		if !ok {
			log.Error("未找到业务服务器数据")
			continue
		}

		func() {
			defer func() {
				if err := recover(); nil != err {
					log.Error("发生异常, %+v", err)
				}
			}()

			log.Info("收到客户端消息并转发")

			// 创建内部服务器消息
			innerCmd := &msg.InnerCmd{
				GatewayServerId: 0,
				SessionUId:      SELF.sessionUId,
				UserId:          SELF.GetUserId(),
				MsgData:         msgData,
			}

			innerCmdByteArray, _ := innerCmd.ToByteArray()

			// 将客户端消息转发给游戏服
			if err = bizServerData.WsConn.WriteMessage(msgType, innerCmdByteArray); nil != err {
				log.Error("转发消息失败, %+v", err)
			}
		}()
	}
}
